package Rpmlib;

use 5.00503;
use strict;
use DynaLoader;
use Cwd qw/realpath/;
use File::Basename qw/basename dirname/;
use File::Spec ();

use vars qw/$VERSION/;
$VERSION = '1.0';
use vars qw/@ISA/;
@ISA = qw/DynaLoader/;

bootstrap Rpmlib $VERSION;

foreach my $tag (keys %Rpmlib::constants) {
  my $sub = q {
    sub [[method]] {
      my $self = shift;
      return $Rpmlib::constants{[[tag]]};
    }
  };

  my $method = lc $tag;
  $method =~ s/^rpm//;
  $sub =~ s/\[\[method\]\]/$method/g;
  $sub =~ s/\[\[tag\]\]/$tag/g;
  eval $sub;

  if ($@) {
    die $@;
  }
}

sub open_rpm_db {
  my $class = shift;
  my %params = @_;

  my $self = bless { }, "Rpmlib::DB";
  if ($params{-path}) {
    $class->add_macro("_dbpath", $params{-path});
    $self->{c_db} = Rpmlib::_open_rpm_db($params{-readwrite} ? 1 : 0);
    $class->delete_macro("_dbpath");
  }
  else {
    $self->{c_db} = Rpmlib::_open_rpm_db($params{-readwrite} ? 1 : 0);
  }

  return $self;
}

sub open_hdlist {
  my $class = shift;
  my $file = shift;

  open FH, "<$file"
    or die "Can't open $file: $!";

  my @ret;
  while (1) {
    my ($hdr) = Rpmlib::_read_from_file(*FH);
    last unless $hdr;

    push @ret, Rpmlib::Header->_new_raw($hdr);
  }

  close FH;
  return @ret;
}

sub open_package {
  my $class = shift;
  my $file = shift;
  my $flags = shift;

  $flags = Rpmlib->vsf_default;
  $flags ||= 0;

  open FH, "<$file"
    or die "Can't open $file: $!";

  my $hdr = Rpmlib::_read_package_info(*FH, $flags);
  close FH;

  my ($filename, $path) = (basename($file), realpath(dirname($file)));
  $hdr = Rpmlib::Header->_new_raw($hdr, File::Spec->catfile($path, $filename));
  return $hdr;
}

sub create_transaction
{
  my $class = shift;
  my $flags = shift;
  my $t;

  if(not defined $flags) {
    $flags = Rpmlib->vsf_default;
  }

  $t = Rpmlib::_create_transaction($flags);
  $t = Rpmlib::Transaction->_new_raw($t);

  return $t;	
}


package Rpmlib::Header;

use overload '<=>'  => \&op_spaceship,
             'cmp'  => \&op_spaceship,
             'bool' => \&op_bool;

sub _new_raw {
  my $class = shift;
  my $c_header = shift;
  my $filename = shift;
  my $offset   = shift;

  my $self = bless { }, $class;
  $self->{c_header}  = $c_header;
  $self->{filename}  = $filename if defined $filename;
  $self->{db_offset} = $offset   if defined $offset;

  return $self;
}

sub tag {
  my $self = shift;
  my $tag = shift;

  $tag = uc "RPMTAG_$tag";

  die "tag $tag invalid"
    unless exists $Rpmlib::header_tag_map{$tag};

  return $self->{c_header}->tag_by_id($Rpmlib::header_tag_map{$tag});
}

sub tagformat {
  my $self   = shift;
  my $format = shift;

  return Rpmlib::C::Header::_header_sprintf($self->{c_header}, $format);
}

sub compare {
  my $h1 = shift;
  my $h2 = shift;

  return Rpmlib::C::Header::_header_compare($h1->{c_header}, $h2->{c_header});
}

sub op_bool {
  my $self = shift;

  return defined($self) && defined($self->{c_header});
}

sub op_spaceship {
  my $h1 = shift;
  my $h2 = shift;

  my $ret = $h1->compare($h2);

  # rpmvercmp can return any neg/pos number; normalize here to -1, 0, 1
  return  1 if $ret > 0;
  return -1 if $ret < 0;
  return  0;
}

sub is_source_package {
  my $self = shift;

  return Rpmlib::C::Header::_header_is_source($self->{c_header});
}

sub filename {
  my $self = shift;
  if (exists $self->{filename}) {
    return $self->{filename};
  }
  return;
}

sub offset {
  my $self = shift;
  if (exists $self->{db_offset}) {
    return $self->{db_offset};
  }
  return;
}

sub as_nvre {
  my $self = shift;
  my $epoch = $self->tag('epoch');
  my $epoch_str = '';

  $epoch_str = "$epoch:" if defined $epoch;

  my $ret = $epoch_str . join("-", map { $self->tag($_) } qw/name version release/);

  return $ret;
}

foreach my $tag (keys %Rpmlib::header_tag_map) {
  $tag =~ s/^RPMTAG_//g;

  my $sub = q {
    sub [[method]] {
      my $self = shift;
      return $self->tag("[[tag]]");
    }
  };

  my $method = lc $tag;
  $sub =~ s/\[\[method\]\]/$method/g;
  $sub =~ s/\[\[tag\]\]/$tag/g;
  eval $sub;

  if ($@) {
    die $@;
  }
}

sub files {
  my $self = shift;

  if (not exists $self->{files}) {
    my @base_names = $self->tag('basenames');
    my @dir_names = $self->tag('dirnames');
    my @dir_indexes = $self->tag('dirindexes');

    my @files;
    foreach (0 .. $#base_names) {
      push @files, $dir_names[$dir_indexes[$_]] . $base_names[$_];
    }

    $self->{files} = \@files;
  }

  return @{$self->{files}};
}

sub changelog {
  my $self = shift;

  if (not exists $self->{changelog}) {
    my @cltimes = $self->tag('CHANGELOGTIME');
    my @clnames = $self->tag('CHANGELOGNAME');
    my @cltexts = $self->tag('CHANGELOGTEXT');

    my @changelog;
    foreach (0 .. $#cltimes) {
      push(@changelog,
           { time => $cltimes[$_],
             name => $clnames[$_],
             text => $cltexts[$_],
           });
    }

    $self->{changelog} = \@changelog;
  }

  return @{$self->{changelog}};
}

package Rpmlib::PackageIterator;

sub new_iterator {
  my $class = shift;
  my $db = shift;
  my $tag = shift;
  my $key = shift;

  my $self = bless { db => $db }, $class;
  $self->{c_iter} = Rpmlib::C::DB::_init_iterator($db->{c_db},
						$Rpmlib::header_tag_map{$tag},
						$key || "",
						defined $key ? length $key : 0);
  return $self;
}

sub next {
  my $self = shift;

  return unless $self->{c_iter};
  my ($hdr, $offset) = $self->{c_iter}->_iterator_next();
  return unless $hdr;

  my $ret = Rpmlib::Header->_new_raw($hdr, undef, $offset);
  return $ret;
}

sub expand_iter {
  my $self = shift;

  my @ret;
  while (my $h = $self->next) {
    push @ret, $h;
  }

  return @ret;
}

# make sure c_iter is destroyed before {db} so that we always free an
# iterator before we free the db it came from

sub DESTROY {
  my $self = shift;
  delete $self->{c_iter};
}


1;
